package com.yshkj.payment.entity;

import org.springframework.security.core.GrantedAuthority;

import javax.persistence.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by 唐旭 on 2016/9/22.
 */
@Entity
@Table
@DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING, name = "_type")
public abstract class Account extends BaseDomain implements UserAccountInfo {
    //base field
    @Column
    private String username;
    @Column
    private String password;
    //是否被锁定
    @Column
    private boolean locked = true;
    //密码是否可用
    @Column
    private boolean credentialsValid = true;
    //账号是否启用
    @Column
    private boolean enable = true;

    //拥有角色
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "user_account")
    private List<UserAccountRole> roleList = new ArrayList<>();

    //拥有权限
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "user_account")
    private Set<UserPrivilege> privilegeHashSet = new HashSet<>();


    public Account() {
    }

    /**
     * Returns the authorities granted to the user. Cannot return <code>null</code>.
     *
     * @return the authorities, sorted by natural key (never <code>null</code>)
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Set<GrantedAuthority> authorities = new HashSet<>();
        authorities.addAll(privilegeHashSet);
        for (UserAccountRole userAccountRole : roleList) {
            authorities.addAll(userAccountRole.getUserRole().getPrivilegeSet());
        }
        return authorities;
    }

    /**
     * Returns the password used to authenticate the user.
     *
     * @return the password
     */
    @Override
    public String getPassword() {
        return this.password;
    }

    public <T extends Account> T setPassword(String password) {
        this.password = password;
        return (T) this;
    }

    /**
     * Returns the username used to authenticate the user. Cannot return <code>null</code>
     * .
     *
     * @return the username (never <code>null</code>)
     */
    @Override
    public String getUsername() {
        return this.username;
    }

    public <T extends Account> T setUsername(String username) {
        this.username = username;
        return (T) this;
    }

    /**
     * Indicates whether the user's account has expired. An expired account cannot be
     * authenticated.
     *
     * @return <code>true</code> if the user's account is valid (ie non-expired),
     * <code>false</code> if no longer valid (ie expired)
     */
    @Override
    public boolean isAccountNonExpired() {
        return this.enable;
    }

    /**
     * Indicates whether the user is locked or unlocked. A locked user cannot be
     * authenticated.
     *
     * @return <code>true</code> if the user is not locked, <code>false</code> otherwise
     */
    @Override
    public boolean isAccountNonLocked() {
        return this.locked;
    }

    /**
     * Indicates whether the user's credentials (password) has expired. Expired
     * credentials prevent authentication.
     *
     * @return <code>true</code> if the user's credentials are valid (ie non-expired),
     * <code>false</code> if no longer valid (ie expired)
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsValid;
    }

    /**
     * Indicates whether the user is enabled or disabled. A disabled user cannot be
     * authenticated.
     *
     * @return <code>true</code> if the user is enabled, <code>false</code> otherwise
     */
    @Override
    public boolean isEnabled() {
        return enable;
    }

    public boolean isLocked() {
        return locked;
    }

    public <T extends Account> T setLocked(boolean locked) {
        this.locked = locked;
        return (T) this;
    }

    public boolean isCredentialsValid() {
        return credentialsValid;
    }

    public <T extends Account> T setCredentialsValid(boolean credentialsValid) {
        this.credentialsValid = credentialsValid;
        return (T) this;
    }

    public boolean isEnable() {
        return enable;
    }

    public <T extends Account> T setEnable(boolean enable) {
        this.enable = enable;
        return (T) this;
    }

    public List<UserAccountRole> getRoleList() {
        return roleList;
    }

    public <T extends Account> T setRoleList(List<UserAccountRole> roleList) {
        this.roleList = roleList;
        return (T) this;
    }

    public Set<UserPrivilege> getPrivilegeHashSet() {
        return privilegeHashSet;
    }

    public <T extends Account> T setPrivilegeHashSet(Set<UserPrivilege> privilegeHashSet) {
        this.privilegeHashSet = privilegeHashSet;
        return (T) this;
    }

    /**
     * 获取一个树形结构的权限列表
     * 例如:<br/>
     * 一级权限<br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;一级权限下的二级 <br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;一级权限下的二级 <br/>
     * 二级权限 <br/>
     *
     * @return 树形权限列表
     */
    public List<Privilege> getPrivilegeTree() {
        Set<Privilege> privileges = getAllPrivilege();
        List<Privilege> privilegeList = sortPrivilege(privileges);
        List<Privilege> rootPrivilege = packageTree(privilegeList);
        return rootPrivilege;
    }

    /**
     * 将权限组装为树形结构
     *
     * @param privilegeList 权限列表
     * @return 树形结构权限列表
     */
    private List<Privilege> packageTree(List<Privilege> privilegeList) {
        List<Privilege> rootPrivilege = new ArrayList<>();
        for (Privilege privilege : privilegeList) {
            if (privilege.getParent() == null) {
                rootPrivilege.add(privilege);
            }
        }
        return rootPrivilege;
    }

    /**
     * 对权限列表进行排序
     * 排序规则:根据Privilege中的枚举类的先后顺序进行排序
     *
     * @param privileges 权限列表
     * @return 排序后的权限列表
     */
    private List<Privilege> sortPrivilege(Set<Privilege> privileges) {
        List<Privilege> privilegeList = new ArrayList<>(privileges);
        privilegeList.sort((o1, o2) -> o1.getOrdered() - o2.getOrdered());
        return privilegeList;
    }

    /**
     * 获取账号所有权限
     *
     * @return 权限列表
     */
    private Set<Privilege> getAllPrivilege() {
        Set<Privilege> privileges = new HashSet<>();
        for (UserAccountRole userAccountRole : roleList) {
            Set<UserPrivilege> privilegeSet = userAccountRole.getUserRole().getPrivilegeSet();
            privileges.addAll(privilegeSet.stream().map(UserPrivilege::getPrivilege).collect(Collectors.toList()));
        }
        privileges.addAll(privilegeHashSet.stream().map(UserPrivilege::getPrivilege).collect(Collectors.toList()));
        return privileges;
    }
}
